/*
 *
 *  * Copyright (c) iwindplus Technologies Co., Ltd.2024-2030, All rights reserved.
 *
 *
 */

package com.iwindplus.base.util;

import cn.hutool.core.util.PrimitiveArrayUtil;
import com.iwindplus.base.domain.constant.CommonConstant;
import com.iwindplus.base.domain.enums.BizCodeEnum;
import com.iwindplus.base.domain.exception.BizException;
import com.iwindplus.base.util.domain.constant.UtilConstant.GenericObjectPoolConstant;
import com.iwindplus.base.util.handler.PooledProtostuffFactory;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

/**
 * Protostuff工具类.
 *
 * @author zengdegui
 * @since 2024
 */
@Slf4j
public class ProtostuffUtil {

    private ProtostuffUtil() {
        throw new IllegalStateException(CommonConstant.UTILITY_CLASS);
    }

    /**
     * 缓存Schema.
     */
    private static final Map<Class<?>, Schema<?>> SCHEMA_CACHE =
        Collections.synchronizedMap(new LinkedHashMap<>(64, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<Class<?>, Schema<?>> eldest) {
                return size() > 256;
            }
        });

    /**
     * LinkedBuffer 对象池.
     */
    private static final AtomicReference<GenericObjectPool<LinkedBuffer>> BUFFER_POOL = new AtomicReference<>();

    private static final AtomicBoolean BUILD_FLAG = new AtomicBoolean(false);

    /**
     * 获取GenericObjectPool.
     *
     * @return GenericObjectPool<LinkedBuffer>
     */
    private static GenericObjectPool<LinkedBuffer> getPool() {
        return BUFFER_POOL.updateAndGet(pool -> {
            if (pool != null) {
                return pool;
            }

            log.warn("BUFFER_POOL is null, build default pool urgently");
            return buildPool();
        });
    }

    /**
     * 设置 GenericObjectPool.
     *
     * @param pool 池配置
     */
    public static void setPool(GenericObjectPool<LinkedBuffer> pool) {
        if (BUILD_FLAG.compareAndSet(false, true)) {
            BUFFER_POOL.set(pool);
            log.info("Protostuff buffer pool build success");
            return;
        }

        log.warn("Protostuff buffer pool already built, later config ignored");
    }

    /**
     * 清 Schema 缓存（方便热更新）.
     */
    public static void clearSchemaCache() {
        SCHEMA_CACHE.clear();
    }

    /**
     * 将对象序列化为字节数组.
     *
     * @param obj 待序列化对象，允许为 {@code null}
     * @param <T> 对象类型
     * @return byte[]
     */
    public static <T> byte[] toJsonBytes(T obj) {
        if (Objects.isNull(obj)) {
            return new byte[0];
        }

        LinkedBuffer buf = borrowBuffer();
        try {
            return ProtostuffIOUtil.toByteArray(obj, getSchema((Class<T>) obj.getClass()), buf);
        } catch (Exception e) {
            log.error("Protostuff execute error", e);
            throw new BizException(BizCodeEnum.SERIALIZE_ERROR);
        } finally {
            release(buf);
        }
    }

    /**
     * 将字节数组反序列化为对象.
     *
     * @param bytes 字节数组，允许为 {@code null} 或空数组
     * @param clazz 目标类
     * @param <T>   类型参数
     * @return T
     */
    public static <T> T parseBytes(byte[] bytes, Class<T> clazz) {
        if (PrimitiveArrayUtil.isEmpty(bytes)) {
            return null;
        }

        try {
            Schema<T> schema = getSchema(clazz);
            T instance = schema.newMessage();
            ProtostuffIOUtil.mergeFrom(bytes, instance, schema);
            return instance;
        } catch (Exception ex) {
            log.error("Protostuff deserialize error, clazz={}", clazz.getSimpleName(), ex);
            throw new BizException(BizCodeEnum.DESERIALIZE_ERROR);
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getSchema(Class<T> clazz) {
        return (Schema<T>) SCHEMA_CACHE.computeIfAbsent(clazz, RuntimeSchema::createFrom);
    }

    public static LinkedBuffer borrowBuffer() {
        try {
            return getPool().borrowObject();
        } catch (Exception ex) {
            log.error("getBuffer error={}", ex.getMessage(), ex);
            throw new BizException(BizCodeEnum.GET_BUFFER_ERROR);
        }
    }

    private static GenericObjectPool<LinkedBuffer> buildPool() {
        GenericObjectPoolConfig<LinkedBuffer> cfg = new GenericObjectPoolConfig<>();
        cfg.setMaxTotal(GenericObjectPoolConstant.DEFAULT_MAX_TOTAL);
        cfg.setMaxIdle(GenericObjectPoolConstant.DEFAULT_MAX_IDLE);
        cfg.setMinIdle(GenericObjectPoolConstant.DEFAULT_MIN_IDLE);
        cfg.setMaxWait(GenericObjectPoolConstant.DEFAULT_MAX_WAIT);
        cfg.setTestOnBorrow(false);
        cfg.setTestOnReturn(false);
        cfg.setBlockWhenExhausted(true);
        return new GenericObjectPool<>(new PooledProtostuffFactory(GenericObjectPoolConstant.DEFAULT_BUFFER_SIZE), cfg);
    }

    /**
     * 归还到池中.
     *
     * @param buf 之前借出的缓冲区（允许 null）
     */
    public static void release(LinkedBuffer buf) {
        if (buf == null) {
            return;
        }

        try {
            getPool().returnObject(buf);
        } catch (Exception e) {
            log.error("return LinkedBuffer error", e);
        }
    }
}